package cn.lunatic.demo;

import java.io.File;
import java.io.Serializable;
import java.io.StringReader;
import java.util.List;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Text;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.jdom.xpath.XPath;

/**
 * @Description:
 * @author ganfeng
 * @date 2014-3-5 下午02:14:38
 */
public class JDomAnalytical implements Serializable{

	private static final long serialVersionUID = -4862883626053603139L;
	
	private final static Logger logger = Logger.getLogger(JDomAnalytical.class);// 获取
	// LOG4J
	static final String ATTR_NAME_DEF = "id"; // 缺省的属性ID,多个相同标签的节点,可作为唯一值判断
	static final String ROOT_NAME = "lunatic"; // 输出实例

	private Document doc;
	private Element root;

	public Document getDoc() {
		return doc;
	}

	public Element getRoot() {
		return root;
	}

	/**
	 * 
	 * 功能：递归添加一个目录下的所有xml文件
	 * 
	 * @param dir
	 */
	public void addDir(String dir) {
		if (!StringUtils.isBlank(dir)) {
			File configFile = new File(dir);
			if (!configFile.exists()) {
				logger.info("文件" + "：(" + dir + ") 不存在");
				throw new RuntimeException("文件" + "：(" + dir + ") 不存在");
			}
			recuAddDir(new File(dir));
		} else {
			throw new RuntimeException("文件不能为空串！");
		}
	}

	/**
	 * 递归查询目录
	 */
	public void recuAddDir(File dir) {
		File[] listFiles = dir.listFiles();
		for (int i = 0; i < listFiles.length; i++) {
			File file = listFiles[i];
			// 如果是目录，继续递归
			if (file.isDirectory()) {
				recuAddDir(file);
			} else if (file.isFile()) {
				String filePath = file.getPath();
				if (filePath.endsWith(".xml")) {
					logger.info("add file: " + filePath);
					JDomAnalytical jdom = new JDomAnalytical();
					Element newRoot = jdom.parseFile(filePath);
					recuAddNode(root, newRoot);
				}
			}
		}
	}

	/**
	 * 功能：将指定的xml文件解释成Element
	 * 
	 * @param xmlFile
	 *            文件名
	 * @return Element
	 */
	public Element parseFile(String xmlFile) {
		try {
			SAXBuilder builder = new SAXBuilder();
			Document doc = builder.build(xmlFile);
			root = doc.getRootElement();
			removeNameSapce(); // 去除命名空间
		} catch (Exception e) {
			logger.info("解释xml文件出错:" + xmlFile, e);
		}
		return root;
	}

	/**
	 * 
	 * 功能：去除toruk根节点命名空间
	 */
	public void removeNameSapce() {
		if (root == null) {
			return;
		}
		String nameSpace = root.getNamespaceURI();
		if (StringUtils.isBlank(nameSpace)) {
			return;
		}
		try {
			String string = JDomAnalytical.formatXml(root, "utf8");
			String regEx = "<" + ROOT_NAME + "[^>]+>";
			Pattern p = Pattern.compile(regEx);
			Matcher m = p.matcher(string);
			String s = m.replaceAll("<" + ROOT_NAME + ">");

			parse(s);
		} catch (Exception e) {
			logger.info("去除xml命名空间出错", e);
		}

	}

	/**
	 * 
	 * 功能：初始分析xml Element
	 * 
	 * @param node
	 * @return 0：成功，否则：失败
	 */
	public Element parse(Element node) {
		root = node;
		return root;
	}

	/**
	 * 功能：初始分析xml字符串,返回Element
	 * 
	 * @param xmlStr
	 *            xml文本串
	 * @return Element
	 */
	public Element parse(String xmlStr) {
		try {
			SAXBuilder builder = new SAXBuilder();
			doc = builder.build(new StringReader(xmlStr));
			root = doc.getRootElement();
		} catch (Exception e) {
			logger.info("解释xml数据出错:" + xmlStr, e);
			// AreaErr.raise("eSys0007", e, xmlStr);
		}
		return root;
	}

	/**
	 * 
	 * 功能：格式化子节点文本输出
	 * 
	 * @param element
	 *            子节点
	 * @param encode
	 *            字符集
	 * @return 格式化串
	 */
	public static String formatXml(Element element, String encode) {
		XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
		String result = "";
		if (null != element) {
			result = outputter.outputString(element);
		}
		return result;
	}

	public void recuAddNode(Element eMain, Element eNew) {
		if (eMain == null) {
			parse(eNew);
			return;
		}
		List<Element> children = eNew.getChildren();
		int i = 0;
		for (;;) {

			// 检查节点是否遍历完
			if (i >= children.size()) {
				break;
			}

			// 取主类的克隆子类
			Element el = children.get(i);
			Element mainCloneChild = getMainCloneChild(eMain, el);

			// 如果存在克隆子类,则递归进子类重新比较,否则,直接将子节点加入主节点树
			if (mainCloneChild != null) {
				i++;
				recuAddNode(mainCloneChild, el);
			} else {
				// 注意: 节点分离后,children.size()会发生变化
				el.detach();
				eMain.addContent(el);
			}
		}
	}

	/**
	 * 
	 * 功能：判断当前节点,是否是主类父节点下的能共存的兄弟节点 1.当前节点无属性值,如果节点名与主类父节点的孩子节点名相同,则不是能共存的兄弟节点
	 * 2.节点属性中，id属性相同的，抛出异常 3.其他都是能共存的
	 * 
	 * @param mainParent
	 * @param node
	 * @return
	 */
	public static Element getMainCloneChild(Element mainParent, Element node) {
		List<Element> children = mainParent.getChildren();
		for (int i = 0; i < children.size(); i++) {
			Element el = children.get(i);

			// 比较节点名
			if (el.getName().equalsIgnoreCase(node.getName())) {

				if (!el.getName().equals(node.getName())) {
					logger.info("存在大小写不一致的同名节点：" + el.getName() + " <--> "
							+ node.getName());
				}

				if (el.getAttributes().size() == 0) {
					return el;
				} else {
					String elId = el.getAttribute("id").getValue();
					String nodeId = node.getAttribute("id").getValue();
					if (elId == null || nodeId == null) {
						return null;
					}

					elId = elId.trim();
					nodeId = nodeId.trim();
					if (elId.equals(nodeId)) {
						logger.info("存在元素id相同的配置：" + elId);
					}
				}
			}
		}
		return null;

	}

	public static Object xpthGetNode(Element element, String exp) {
		Object o = null;
		try {
			o = XPath.selectSingleNode(element, exp);
		} catch (JDOMException e) {
			logger.info("xpath[" + exp + "]取值异常", e);
		}
		return o;
	}

	/**
	 * 
	 * 功能：通过xpath表达式获取值
	 * 
	 * @param element
	 *            数据源元素
	 * @param exp
	 *            xpath表达式
	 * @return String 值
	 */
	public static String xpathVal(Element element, String exp) {
		String ret = "";
		try {
			Object o = XPath.selectSingleNode(element, exp);
			if (o == null) {
				return ret;
			}
			if (o instanceof Text) {
				ret = ((Text) o).getTextNormalize();
			} else if (o instanceof Attribute) {
				ret = ((Attribute) o).getValue();
			} else if (o instanceof Element) {
				ret = ((Element) o).getTextTrim();
			} else {
				ret = o.toString();
			}

		} catch (Throwable e) {
			logger.info("xpath取值异常", e);
			logger.info("xpath表达式：" + exp);
			logger.info(formatXml(element, "utf-8"));
		}
		return ret;
	}

	/**
	 * 功能：通过xpath表达式从一个元素中获取匹配的子元素列表。
	 * 
	 * @param element
	 * @param exp
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static List<Element> xpathGetNodes(Element element, String exp) {
		List<Element> nodes = new Vector<Element>();
		try {
			nodes = (List<Element>) XPath.selectNodes(element, exp);
		} catch (Throwable e) {
			logger.info("xpath取值异常", e);
			logger.info("xpath表达式：" + exp);
			logger.info(formatXml(element, "utf-8"));
		}
		return nodes;
	}

	@SuppressWarnings("unchecked")
	public static void main(String[] args) {
		JDomAnalytical jdom = new JDomAnalytical();
		jdom.addDir("config" + File.separator + "JDBC");
		
		Element curr_root = jdom.getRoot();
		System.out.println(curr_root);
		
		List<Element> childrens = ((Element)curr_root.getChildren("sqls").get(0)).getChildren();
		
		for(Element e : childrens){
			System.out.println(e.toString());
		}
		
	}
}
